from decimal import Decimal
from numpy import matrix, ndarray

class ComplexDecimal:
    def __init__(self, real, imag):
        if isinstance(real, float):
            self.real = Decimal(str(real))
        elif not isinstance(real,Decimal):
            self.real = Decimal(real)
        else:
            self.real = real
        if isinstance(imag, float):
            self.imag = Decimal(str(imag))
        elif not isinstance(imag,Decimal):
            self.imag = Decimal(imag)
        else:
            self.imag = imag
    def __add__(self, y):
        """x.__add__(y) <==> x+y"""
        if isinstance(y, (float,complex)):
            return ComplexDecimal(self.real+Decimal(str(y.real)),
                                  self.imag+Decimal(str(y.imag)))
        else:
            return ComplexDecimal(self.real+y.real, self.imag+y.imag)
    def __sub__(self, y):
        """x.__sub__(y) <==> x-y"""
        if isinstance(y,(float,complex)):
            return ComplexDecimal(self.real-Decimal(str(y.real)),
                                  self.imag-Decimal(str(y.imag)))
        else:
            return ComplexDecimal(self.real-y.real, self.imag-y.imag)
    def __mul__(self, y):
        """x.__mul__(y) <==> x*y"""
        if isinstance(y,(float,complex)):
            return ComplexDecimal(self.real*Decimal(str(y.real))-
                                  self.imag*Decimal(str(y.imag)),
                                  self.real*Decimal(str(y.imag))+
                                  self.imag*Decimal(str(y.imag)))
        elif not isinstance(y,matrix):
            return ComplexDecimal(self.real*y.real-self.imag*y.imag,
                                  self.real*y.imag+self.imag*y.real)
        else:
            result = ndarray(y.shape,object)
            for i in range(y.shape[0]):
                for j in range(y.shape[1]):
                    result[i,j] = self*y[i,j]
            return matrix(result)
    def __div__(self,y):
        # We need to work with this and matrices...
        """x.__div__(y) <==> x/y"""
        if isinstance(y,float):
            return ComplexDecimal(self.real/Decimal(str(y)),
                                  self.imag/Decimal(str(y)))
        elif isinstance(y,(int,Decimal)):
            return ComplexDecimal(self.real/y,self.imag/y)
        elif isinstance(y,complex):
            n = self*ComplexDecimal(y.real,-y.imag)
            d = Decimal(str(y.real))**2+Decimal(str(y.imag))**2
            return n/d
        elif isinstance(y,ComplexDecimal):
            n = self*y.conjugate()
            d = y.real**+y.imag**2
            return n/d
    def __neg__(self):
        """x.__neg__() <==> -x"""
        return ComplexDecimal(-self.real,-self.imag)
    def conjugate(self):
        return ComplexDecimal(self.real,-self.imag)
    def __mod__(self,y):
        """x.__mod__(y) <==> x%y"""
        if isinstance(y,float) or y.imag == 0:
            return ComplexDecimal(self.real%Decimal(str(y)),self.imag)
        elif isinstance(y,complex):
            return ComplexDecimal(self.real%Decimal(str(y.real)),
                                  self.real%Decimal(str(y.imag)))
        elif isinstance(y,Decimal) or y.imag == 0:
            return ComplexDecimal(self.real%y,self.imag)
        elif isinstance(y,ComplexDecimal):
            return ComplexDecimal(self.real%y.real,self.imag%y.imag)
    
    def __str__(self):
        """x.__str__() <==> str(x)"""
        if self.imag < 0:
            sign = "-"
        else:
            sign = "+"
        return str(self.real) + sign + str(self.imag) + "j"
    def __radd__(self,y):
        """x.__radd__(y) <==> y+x"""
        return self+y
    def __rsub__(self,y):
        """x.__rsub__(y) <==> y-x"""
        if isinstance(y,(float,conj)):
            return ComplexDecimal(Decimal(str(y.real)-self.real),
                                  Decimal(str(y.imag))-self.imag)
        else:
            return ComplexDecimal(y.real-self.real,y.imag-self.imag)
    def __rmul__(self,y):
        """x.__rmul__(y) <==> y*x"""
        return self*y
    def __rdiv__(self,y):
        # We need to work with this and matrices...
        """x.__rdiv__(y) <==> y/x"""
        if isinstance(y,float):
            return ComplexDecimal(y,0)/self
        elif isinstance(y,(int,Decimal)):
            return ComplexDecimal(y,0)/self
        elif isinstance(y,complex):
            return ComplexDecimal(y.real,y.imag)/self
        elif isinstance(y,ComplexDecimal):
            return y.__div__(self)
    def __pow__(self,y):
        """x.__pow__(y) <==> x**y"""
        c = 0
        result = 1
        while c < y:
            result *= self
            c += 1
        return result
    def __abs__(self):
        """x.__abs__() <==> abs(x)"""
        return Decimal.sqrt(self.real**2+self.imag**2)
    
